package io.openmessaging.storage.dledger;

import io.openmessaging.storage.dledger.utils.ResettableCountDownLatch;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;

//
public abstract class ShutdownAbleThread extends Thread {

    protected Logger logger;

    //支持重置复用的同步器
    protected final ResettableCountDownLatch waitPoint = new ResettableCountDownLatch(1);

    //是否被通知过 : 为 false 说明没有被通知 不执行之后逻辑 ; 为 true 说明被通知了  执行之后逻辑 并再次置为 false  以达到复用效果 ;
    //用来控制 waitPoint 调用次数 , 但是真的性价比 很高吗???
    protected volatile AtomicBoolean hasNotified = new AtomicBoolean(false);

    //当前线程是否还在正常运行
    private AtomicBoolean running = new AtomicBoolean(true);

    //目的 : 控制当前线程 shutdown() 时 , doWork() 能够尽可能的正常执行完毕
    private CountDownLatch latch = new CountDownLatch(1);

    public ShutdownAbleThread(String name, Logger logger) {
        super(name);
        this.logger = logger;

    }

    //线程关闭流程
    public void shutdown() {
        //可以 执行 关闭流程
        if (running.compareAndSet(true, false)) {
            try {
                // waitPoint.countDown(); // notify 唤醒执行 waitForRunning() 的线程
                wakeup();
                //先阻塞住10秒  等待被唤醒 ; 问 : 在等待什么 ??  答 : 等待93行代码被执行 , 上下文是 等待 doWork() 方法正常执行完毕 .
                latch.await(10, TimeUnit.SECONDS);
            } catch (Throwable t) {
                if (logger != null) {
                    logger.error("Unexpected Error in shutting down {} ", getName(), t);
                }
            }
            if (latch.getCount() != 0) {
                if (logger != null) {
                    logger.error("The {} failed to shutdown in {} seconds", getName(), 10);
                }
            }
        }
    }

    public abstract void doWork();

    //唤醒 waitPoint 同步器操作
    public void wakeup() {
        //如果成功唤醒
        if (hasNotified.compareAndSet(false, true)) {
            waitPoint.countDown(); // notify 此方法 多次调用 也是无事发生的
            //此时 执行 waitForRunning() 的线程 就能复活了 继续执行之后的逻辑了
        }
    }

    //执行阻塞线程操作
    public void waitForRunning(long interval) throws InterruptedException {
        //期望是 true 然后设置成 false 说明什么 ???  说明之前已经被通知过了  那就直接退出 继续设置 false 来等待被通知
        if (hasNotified.compareAndSet(true, false)) {
            // 通知过 就走 , 按次 来发挥作用 , 而不是按照实时性
            // 因为 hasNotified 默认是 false , 假如有一个线程 先执行了 wakeup() 方法而不是 waitForRunning() , 那么另外一个线程执行waitForRunning()时,
            //就会收到 wakeup() 方法的影响 , 直接退出 , 而不用阻塞 ;
            return;
        }
        //entry to wait
        waitPoint.reset();
        try {
            waitPoint.await(interval, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            logger.error("The {} is interrupted", getName(), e);
            throw e;
        } finally {
            hasNotified.set(false);
        }
    }

    @Override
    public void run() {
        //死循环
        while (running.get()) {
            try {
                //循环的调用这个方法
                doWork();
            } catch (Throwable t) {
                if (logger != null) {
                    logger.error("Unexpected Error in running {} ", getName(), t);
                }
            }
        }//while over
        //唤醒 latch.await(10, TimeUnit.SECONDS); 处线程
        latch.countDown();
    }

    public Logger getLogger() {
        return logger;
    }

    public void setLogger(Logger logger) {
        this.logger = logger;
    }

}
